package cn.springcloud.fix.common.starter.utils;

import cn.springcloud.fix.common.core.utils.*;
import cn.springcloud.fix.common.core.utils.digest.Base64Util;
import cn.springcloud.fix.common.core.utils.digest.DigestUtil;
import cn.springcloud.fix.common.core.utils.json.JsonUtil2;
import cn.springcloud.fix.common.core.utils.support.ConcurrentDateFormat;
import cn.springcloud.fix.common.starter.utils.net.UrlUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONPath;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.base.MoreObjects;
import org.springframework.beans.BeansException;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;

import java.io.Closeable;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.time.Duration;
import java.time.format.DateTimeFormatter;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.util.*;

public class SpringCloudUtil {

    public static String md5Hex(final String data) {
        return DigestUtil.md5Hex(data);
    }

    public static String md5Hex(final byte[] bytes) {
        return DigestUtil.md5Hex(bytes);
    }

    public static String sha1(String srcStr) {
        return DigestUtil.sha1(srcStr);
    }

    public static String sha256(String srcStr) {
        return DigestUtil.sha256(srcStr);
    }

    public static String sha384(String srcStr) {
        return DigestUtil.sha384(srcStr);
    }

    public static String sha512(String srcStr) {
        return DigestUtil.sha512(srcStr);
    }

    /**
     * 自定义加密 先MD5再SHA1
     */
    public static String encrypt(String data) {
        return DigestUtil.encrypt(data);
    }

    /**
     * 编码
     */
    public static String encodeBase64(String value) {
        return Base64Util.encode(value);
    }

    /**
     * 编码
     */
    public static String encodeBase64(String value, Charset charset) {
        return Base64Util.encode(value, charset);
    }

    /**
     * 编码URL安全
     */
    public static String encodeBase64UrlSafe(String value) {
        return Base64Util.encodeUrlSafe(value);
    }

    /**
     * 编码URL安全
     */
    public static String encodeBase64UrlSafe(String value, Charset charset) {
        return Base64Util.encodeUrlSafe(value, charset);
    }

    /**
     * 解码
     */
    public static String decodeBase64(String value) {
        return Base64Util.decode(value);
    }

    /**
     * 解码
     */
    public static String decodeBase64(String value, Charset charset) {
        return Base64Util.decode(value, charset);
    }

    /**
     * 解码URL安全
     */
    public static String decodeBase64UrlSafe(String value) {
        return Base64Util.decodeUrlSafe(value);
    }

    /**
     * 解码URL安全
     */
    public static String decodeBase64UrlSafe(String value, Charset charset) {
        return Base64Util.decodeUrlSafe(value, charset);
    }

    /**
     * closeQuietly
     */
    public static void closeQuietly(@Nullable Closeable closeable) {
        IOUtil.close(closeable);
    }

    public static String toString(InputStream input) {
        return IOUtil.toString(input);
    }

    public static String toString(@Nullable InputStream input, Charset charset) {
        return IOUtil.toString(input, charset);
    }

    public static byte[] toByteArray(@Nullable InputStream input) {
        return IOUtil.toByteArray(input);
    }

    /**
     * 将对象序列化成json字符串
     */
    public static String toJson(Object object) {
        return JsonUtil2.toJson(object);
    }

    /**
     * 将对象序列化成 json byte 数组
     */
    public static byte[] toJsonAsBytes(Object object) {
        return JsonUtil2.toJsonAsBytes(object);
    }

    /**
     * 将json字符串转成 JsonNode
     */
    public static JsonNode readTree(String jsonString) {
        return JsonUtil2.readTree(jsonString);
    }

    /**
     * 将json字符串转成 JsonNode
     */
    public static JsonNode readTree(InputStream in) {
        return JsonUtil2.readTree(in);
    }

    /**
     * 将json字符串转成 JsonNode
     */
    public static JsonNode readTree(byte[] content) {
        return JsonUtil2.readTree(content);
    }

    /**
     * 将json字符串转成 JsonNode
     */
    public static JsonNode readTree(JsonParser jsonParser) {
        return JsonUtil2.readTree(jsonParser);
    }

    /**
     * 将json byte 数组反序列化成对象
     */
    public static <T> T parse(byte[] bytes, Class<T> valueType) {
        return JsonUtil2.parse(bytes, valueType);
    }

    /**
     * 将json反序列化成对象
     */
    public static <T> T parse(String jsonString, Class<T> valueType) {
        return JsonUtil2.parse(jsonString, valueType);
    }

    /**
     * 将json反序列化成对象
     */
    public static <T> T parse(InputStream in, Class<T> valueType) {
        return JsonUtil2.parse(in, valueType);
    }

    /**
     * 将json反序列化成对象
     */
    public static <T> T parse(byte[] bytes, TypeReference<?> typeReference) {
        return JsonUtil2.parse(bytes, typeReference);
    }

    /**
     * 将json反序列化成对象
     */
    public static <T> T parse(String jsonString, TypeReference<?> typeReference) {
        return JsonUtil2.parse(jsonString, typeReference);
    }

    /**
     * 将json反序列化成对象
     */
    public static <T> T parse(InputStream in, TypeReference<?> typeReference) {
        return JsonUtil2.parse(in, typeReference);
    }

    public static String encode(String source) {
        return UrlUtil.encode(source, CharsetUtil.UTF_8);
    }

    public static String encode(String source, Charset charset) {
        return UrlUtil.encode(source, charset);
    }

    public static String decode(String source) {
        return StringUtils.uriDecode(source, CharsetUtil.UTF_8);
    }

    public static String decode(String source, Charset charset) {
        return StringUtils.uriDecode(source, charset);
    }

    /**
     * 日期时间格式化
     */
    public static String formatDateTime(Date date) {
        return DateUtil.formatDateTime(date);
    }

    /**
     * 日期格式化
     */
    public static String formatDate(Date date) {
        return DateUtil.formatDate(date);
    }

    /**
     * 日期格式化
     */
    public static String format(Date date, String pattern) {
        return DateUtil.format(date, pattern);
    }

    /**
     * 将字符串转换为时间
     */
    public static Date parseDate(String dateStr, String pattern) {
        return DateUtil.parse(dateStr, pattern);
    }

    /**
     * 将字符串转换为时间
     */
    public static Date parse(String dateStr, ConcurrentDateFormat format) {
        return DateUtil.parse(dateStr, format);
    }

    /**
     * 日期时间格式化
     */
    public static String formatDateTime(TemporalAccessor temporal) {
        return DateUtil.formatDateTime(temporal);
    }

    /**
     * 日期时间格式化
     */
    public static String formatDate(TemporalAccessor temporal) {
        return DateUtil.formatDate(temporal);
    }

    /**
     * 时间格式化
     */
    public static String formatTime(TemporalAccessor temporal) {
        return DateUtil.formatTime(temporal);
    }

    /**
     * 日期格式化
     */
    public static String format(TemporalAccessor temporal, String pattern) {
        return DateUtil.format(temporal, pattern);
    }

    /**
     * 将字符串转换为时间
     */
    public static TemporalAccessor parse(String dateStr, String pattern) {
        return DateUtil.parseTemporalAccessor(dateStr, pattern);
    }

    /**
     * 将字符串转换为时间
     */
    public static TemporalAccessor parse(String dateStr, DateTimeFormatter formatter) {
        return DateUtil.parse(dateStr, formatter);
    }

    /**
     * 时间比较
     */
    public static Duration between(Temporal startInclusive, Temporal endExclusive) {
        return Duration.between(startInclusive, endExclusive);
    }

    /**
     * 获取方法参数信息
     */
    public static MethodParameter getMethodParameter(Constructor<?> constructor, int parameterIndex) {
        return ClassUtil.getMethodParameter(constructor, parameterIndex);
    }

    /**
     * 获取方法参数信息
     */
    public static MethodParameter getMethodParameter(Method method, int parameterIndex) {
        return ClassUtil.getMethodParameter(method, parameterIndex);
    }

    /**
     * 获取Annotation
     */
    @Nullable
    public static <A extends Annotation> A getAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
        return AnnotatedElementUtils.findMergedAnnotation(annotatedElement, annotationType);
    }

    /**
     * 获取Annotation
     */
    @Nullable
    public static <A extends Annotation> A getAnnotation(Method method, Class<A> annotationType) {
        return ClassUtil.getAnnotation(method, annotationType);
    }

    /**
     * 获取Annotation
     */
    @Nullable
    public static <A extends Annotation> A getAnnotation(HandlerMethod handlerMethod, Class<A> annotationType) {
        return ClassUtil.getAnnotation(handlerMethod, annotationType);
    }

    /**
     * 实例化对象
     */
    @SuppressWarnings("unchecked")
    public static <T> T newInstance(Class<?> clazz) {
        return (T) BeanUtil.instantiateClass(clazz);
    }

    /**
     * 实例化对象
     */
    public static <T> T newInstance(String clazzStr) {
        return BeanUtil.newInstance(clazzStr);
    }

    /**
     * 获取Bean的属性
     */
    public static Object getProperty(Object bean, String propertyName) {
        return BeanUtil.getProperty(bean, propertyName);
    }

    /**
     * 设置Bean属性
     */
    public static void setProperty(Object bean, String propertyName, Object value) {
        BeanUtil.setProperty(bean, propertyName, value);
    }

    /**
     * 深复制
     */
    public static <T> T clone(T source) {
        return BeanUtil.clone(source);
    }

    /**
     * copy 对象属性到另一个对象，默认不使用Convert
     */
    public static <T> T copy(Object source, Class<T> clazz) {
        return BeanUtil.copy(source, clazz);
    }

    /**
     * 拷贝对象
     */
    public static void copy(Object source, Object targetBean) {
        BeanUtil.copy(source, targetBean);
    }

    public static <T> T copyProperties(Object source, Class<T> clazz) throws BeansException {
        return BeanUtil.copyProperties(source, clazz);
    }

    /**
     * 将对象装成map形式
     */
    public static Map<String, Object> toMap(Object bean) {
        return BeanUtil.toMap(bean);
    }

    /**
     * 将map 转为 bean
     */
    public static <T> T toBean(Map<String, Object> beanMap, Class<T> valueType) {
        return BeanUtil.toBean(beanMap, valueType);
    }

    public static <T> String stringValue(Object object) {
        if (object == null) {
            return "null";
        }
        Class cz = object.getClass();
        MoreObjects.ToStringHelper stringHelper = MoreObjects.toStringHelper(cz);
        if (cz.isArray()) {
            if (cz.getComponentType().isPrimitive()) {
                if (cz == byte[].class) {
                    stringHelper.addValue(Arrays.toString((byte[]) object));
                } else if (cz == short[].class) {
                    stringHelper.addValue(Arrays.toString((short[]) object));
                } else if (cz == int[].class) {
                    stringHelper.addValue(Arrays.toString((int[]) object));
                } else if (cz == long[].class) {
                    stringHelper.addValue(Arrays.toString((long[]) object));
                } else if (cz == char[].class) {
                    stringHelper.addValue(Arrays.toString((char[]) object));
                } else if (cz == float[].class) {
                    stringHelper.addValue(Arrays.toString((float[]) object));
                } else if (cz == double[].class) {
                    stringHelper.addValue(Arrays.toString((double[]) object));
                } else if (cz == boolean[].class) {
                    stringHelper.addValue(Arrays.toString((boolean[]) object));
                }
                return stringHelper.toString();
            } else {
                return cz.getSimpleName() + "{" + Arrays.deepToString((Object[]) object) + "}";
            }
        }
        if (object instanceof List) {
            List ls = ((List) object);
            for (Object obj : ls) {
                stringHelper.addValue(JSON.toJSONString(obj));
            }
            return stringHelper.toString();
        }
        if (object instanceof Set) {
            Set sets = ((Set) object);
            for (Object obj : sets) {
                stringHelper.addValue(JSON.toJSONString(obj));
            }
            return stringHelper.toString();
        }
        if (object instanceof Map) {
            for (Map.Entry<Object, Object> entry : ((Map<Object, Object>) object).entrySet()) {
                stringHelper.add(String.valueOf(entry.getKey()), JSON.toJSONString(entry.getValue()));
            }

            return stringHelper.toString();
        }
        Field fields[] = cz.getDeclaredFields();
        Field.setAccessible(fields, true);

        try {
            for (Field field : fields) {
                stringHelper.add(field.getName(), field.get(object));
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return stringHelper.toString();
    }

    public static boolean isNotEmpty(Collection collection, long size) {
        return collection != null && !collection.isEmpty() && collection.size() == size;
    }

    public static Map<String, Object> getMap(String[] strings, Object... objects) {
        Map<String, Object> map = new HashMap<>();
        if (strings.length > objects.length) {
            return map;
        }
        for (int i = 0; i < strings.length; i++) {
            map.put(strings[i], objects[i]);
        }
        return map;
    }


    /**
     * 将JSONArray转换成Map[{"aa":xx,"cc":xx,"bb":xx},{"aa":xx,"cc":xx,"bb":xx}]
     * 注意一定是ListJson对象转换
     */
    public static Map<Object, Object> jsonPathToMap(final String json, final String jsonPath, final String keyPropertyName, final String valuePropertyName) {
        JSONArray jsonArray = (JSONArray) JSONPath.read(json, jsonPath);
        return CollectionUtil.extractToMap(jsonArray, keyPropertyName, valuePropertyName);
    }

    public static String changeF2Y(Long amount) throws Exception {
        return BigDecimal.valueOf(amount).divide(new BigDecimal(100)).toString();
    }

    public static <T> T findInJson(String json, String key, Class<T> clazz) {
        return (T) JSON.parseObject(json).getObject(key, clazz);
    }

    public static <T> T firstNonNull(T first, T second) {
        if (second == null) {
            throw new NullPointerException();
        }
        return first != null ? first : second;
    }

    /**
     * 费波纳茨
     */
    public static int fibonacci(int number) {
        if ((number == 0) || (number == 1)) {
            return number;
        } else {
            return fibonacci(number - 1) + fibonacci(number - 2);
        }
    }
}
